import React, {
  forwardRef,
  useEffect,
  useImperativeHandle,
  useRef,
  useState,
} from 'react';
import { bound } from '../utils/bound';
import { isIOS } from '../utils/device';
import CloseCircleFill from './close-circle';
import './input.less';

const classPrefix = `bbt-input`;

type NativeInputProps = React.DetailedHTMLProps<
  React.InputHTMLAttributes<HTMLInputElement>,
  HTMLInputElement
>;

type AriaProps = {
  // These props currently are only used internally. They are not exported to users:
  role?: string;
};

export type InputProps = Pick<
  NativeInputProps,
  | 'maxLength'
  | 'minLength'
  | 'autoComplete'
  | 'autoFocus'
  | 'pattern'
  | 'inputMode'
  | 'type'
  | 'name'
  | 'onFocus'
  | 'onBlur'
  | 'autoCapitalize'
  | 'autoCorrect'
  | 'onKeyDown'
  | 'onKeyUp'
  | 'onCompositionStart'
  | 'onCompositionEnd'
  | 'onClick'
  | 'step'
> & {
  className?: string;
  style?: React.CSSProperties & Partial<Record<string | never, string>>;
  value?: string;
  defaultValue?: string;
  onChange?: (val: string) => void;
  placeholder?: string;
  disabled?: boolean;
  readOnly?: boolean;
  clearable?: boolean;
  onlyShowClearWhenFocus?: boolean;
  onClear?: () => void;
  id?: string;
  onEnterPress?: (e: React.KeyboardEvent<HTMLInputElement>) => void;
  enterKeyHint?:
    | 'enter'
    | 'done'
    | 'go'
    | 'next'
    | 'previous'
    | 'search'
    | 'send';
  min?: number;
  max?: number;
} & AriaProps;

export type InputRef = {
  clear: () => void;
  focus: () => void;
  blur: () => void;
  nativeElement: HTMLInputElement | null;
};

const defaultProps = {
  defaultValue: '',
  onlyShowClearWhenFocus: true,
};

const Input = forwardRef<InputRef, InputProps>((p, ref) => {
  const props = { ...defaultProps, ...(p || {}) };
  const [value, setValue] = useState(props?.defaultValue || '');
  const [hasFocus, setHasFocus] = useState(false);
  const compositionStartRef = useRef(false);
  const nativeInputRef = useRef<HTMLInputElement>(null);

  useEffect(() => {
    setValue(props?.value || '');
  }, [props.value]);

  useImperativeHandle(ref, () => ({
    clear: () => {
      setValue('');
    },
    focus: () => {
      nativeInputRef.current?.focus();
    },
    blur: () => {
      nativeInputRef.current?.blur();
    },
    get nativeElement() {
      return nativeInputRef.current;
    },
  }));

  const handleKeydown = (e: React.KeyboardEvent<HTMLInputElement>) => {
    if (props.onEnterPress && (e.code === 'Enter' || e.keyCode === 13)) {
      props.onEnterPress(e);
    }
    props.onKeyDown?.(e);
  };

  useEffect(() => {
    if (!props.enterKeyHint) return;
    nativeInputRef.current?.setAttribute('enterkeyhint', props.enterKeyHint);
    return () => {
      nativeInputRef.current?.removeAttribute('enterkeyhint');
    };
  }, [props.enterKeyHint]);

  function checkValue() {
    let nextValue = value;
    if (props.type === 'number') {
      nextValue =
        nextValue &&
        bound(parseFloat(nextValue), props.min, props.max).toString();
    }
    if (nextValue !== value) {
      setValue(nextValue);
    }
  }

  const shouldShowClear = (() => {
    if (!props.clearable || !value || props.readOnly) return false;
    if (props.onlyShowClearWhenFocus) {
      return hasFocus;
    } else {
      return true;
    }
  })();

  return (
    <div
      className={`${classPrefix} ${props.className || ''} ${
        props.disabled ? `${classPrefix}-disabled` : ''
      }`}
    >
      <input
        ref={nativeInputRef}
        className={`${classPrefix}-element`}
        value={value}
        onChange={(e) => {
          setValue(e.target.value);
          typeof props?.onChange === 'function' &&
            props?.onChange?.(e.target.value);
        }}
        onFocus={(e) => {
          setHasFocus(true);
          props.onFocus?.(e);
        }}
        onBlur={(e) => {
          setHasFocus(false);
          checkValue();
          props.onBlur?.(e);
        }}
        id={props.id}
        placeholder={props.placeholder}
        disabled={props.disabled}
        readOnly={props.readOnly}
        maxLength={props.maxLength}
        minLength={props.minLength}
        max={props.max}
        min={props.min}
        autoComplete={props.autoComplete}
        autoFocus={props.autoFocus}
        pattern={props.pattern}
        inputMode={props.inputMode}
        type={props.type}
        id={props.name}
        autoCapitalize={props.autoCapitalize}
        autoCorrect={props.autoCorrect}
        onKeyDown={handleKeydown}
        onKeyUp={props.onKeyUp}
        onCompositionStart={(e) => {
          compositionStartRef.current = true;
          props.onCompositionStart?.(e);
        }}
        onCompositionEnd={(e) => {
          compositionStartRef.current = false;
          props.onCompositionEnd?.(e);
        }}
        onClick={props.onClick}
        role={props.role}
        aria-valuenow={props['aria-valuenow']}
        aria-valuemax={props['aria-valuemax']}
        aria-valuemin={props['aria-valuemin']}
        aria-label={props['aria-label']}
      />
      {shouldShowClear && (
        <div
          className={`${classPrefix}-clear`}
          onMouseDown={(e) => {
            e.preventDefault();
          }}
          onClick={() => {
            setValue('');
            props.onClear?.();

            // https://github.com/ant-design/ant-design-mobile/issues/5212
            if (isIOS() && compositionStartRef.current) {
              compositionStartRef.current = false;
              nativeInputRef.current?.blur();
            }
          }}
          //   aria-label={locale.Input.clear}
        >
          <CloseCircleFill />
        </div>
      )}
    </div>
  );
});

export { Input };
